﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using ChessAnotator.Base.Business;

namespace ChessAnotator.UserControls
{
    /// <summary>
    /// Interaction logic for Board.xaml
    /// </summary>
    public partial class Board : UserControl, INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnNotifyPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        #region Private Members
        private List<Pieces.BasePiece> m_arrPieces;
        private int? m_nMoveStartSquareNbr = null;
        private int? m_nMoveEndSquareNbr = null;
        #endregion

        #region Properties
        int m_nSquareSize;
        public int SquareSize
        {
            get
            {
                return m_nSquareSize;
            }
            set
            {
                m_nSquareSize = value;
                OnNotifyPropertyChanged("SquareSize");
            }
        }

        public double PieceScale
        {
            get;
            set;
        }

        private double m_nOverlayOpacity;
        public double OverlayOpacity
        {
            get
            {
                return m_nOverlayOpacity;
            }
            set
            {
                m_nOverlayOpacity = value;
                OnNotifyPropertyChanged("OverlayOpacity");
            }
        }
        #endregion

        public Board()
        {
            this.SquareSize = 80;
            this.PieceScale = 0.11;
            this.OverlayOpacity = 1;// 0.5;

            InitializeComponent();

            this.LayoutRoot.DataContext = this;

            InitializeBoard();
        }

        public void InitializeBoard()
        {
            for (int nRow = 0; nRow < Nomenclature.BOARD_SIZE; nRow++)
            {
                for (int nColumn = 0; nColumn < Nomenclature.BOARD_SIZE; nColumn++)
                {
                    Binding oSquareSizeBinding = new Binding
                    {
                        Path = new PropertyPath("SquareSize")
                        , RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Board), 1)
                    };
                    Binding oOverlayOpacityBinding = new Binding
                    {
                        Path = new PropertyPath("OverlayOpacity")
                        , RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Board), 1)
                    };
                    Square oCurrSquare = new Square();
                    oCurrSquare.Name = "sq" + nRow.ToString() + nColumn.ToString();
                    if ((nRow % 2 == 1 && nColumn % 2 == 0)
                        || (nRow % 2 == 0 && nColumn % 2 == 1)
                        )
                    {
                        oCurrSquare.SquareBackground = new SolidColorBrush(Colors.LightGray);
                    }
                    else
                    {
                        oCurrSquare.SquareBackground = new SolidColorBrush(Colors.White);
                    }
                    oCurrSquare.SetBinding(Square.SizeProperty, oSquareSizeBinding);
                    oCurrSquare.SetBinding(Square.OverlayOpacityProperty, oOverlayOpacityBinding);
                    oCurrSquare.SquareSelected += new Square.SquareSelectedEventHandler(Square_SquareSelected);

                    Grid.SetRow(oCurrSquare, nRow);
                    Grid.SetColumn(oCurrSquare, nColumn + 1);
                    LayoutRoot.Children.Add(oCurrSquare);
                }
            }

            // Init board labels
            for (int nLabelsCnt = 0; nLabelsCnt < Nomenclature.BOARD_SIZE; nLabelsCnt++)
            {
                TextBlock txtRank = new TextBlock()
                {
                    Text = (Nomenclature.BOARD_SIZE - nLabelsCnt).ToString()
                    , FontSize = 20
                    , VerticalAlignment = VerticalAlignment.Center
                    , HorizontalAlignment = HorizontalAlignment.Center
                };

                Grid.SetRow(txtRank, nLabelsCnt);
                Grid.SetColumn(txtRank, 0);
                LayoutRoot.Children.Add(txtRank);

                TextBlock txtFile = new TextBlock()
                {
                    Text = ((char)(97 + nLabelsCnt)).ToString()
                    , FontSize = 20
                    , VerticalAlignment = VerticalAlignment.Center
                    , HorizontalAlignment = HorizontalAlignment.Center
                };

                Grid.SetRow(txtFile, 8);
                Grid.SetColumn(txtFile, 1 + nLabelsCnt);
                LayoutRoot.Children.Add(txtFile);
            }
        }

        void Square_SquareSelected(object sender, EventArgs e)
        {
            Square oCurrSquare = (Square)sender;

            if (!m_nMoveStartSquareNbr.HasValue)
            {
                m_nMoveStartSquareNbr = oCurrSquare.SquareNbr;
                return;
            }

            if (HasInterveningPiece(m_nMoveStartSquareNbr.Value, oCurrSquare.SquareNbr))
            {
                return;
            }

            m_nMoveEndSquareNbr = oCurrSquare.SquareNbr;

            MovePiece();
            // TODO: Make the move. If it is a capture remove the captured piece from the m_arrPieces as well.

            int a = 4;
            //throw new NotImplementedException();
        }

        public void LoadPosition(string sFENPosition)
        {
            ClearBoard();

            Position oPosition = new Position(sFENPosition);
            m_arrPieces = oPosition.GetPieces(this.PieceScale);

            foreach (Pieces.BasePiece item in m_arrPieces)
            {
                item.SetScale(this.PieceScale);

                Square oCurrSquare = GetSquare(item.SquareName); //this.FindName(item.Square);            
                oCurrSquare.SetPiece(item);
                //oCurrSquare.WhiteAttackersCount = 10;
                //oCurrSquare.BlackAttackersCount = 15;
            }

            CalculateSquareAttackers();
        }

        private void ClearBoard()
        {
            for (int nSquareNbr = 0; nSquareNbr < Nomenclature.BOARD_SIZE * Nomenclature.BOARD_SIZE; nSquareNbr++)
            {
                Square oCurrSquare = GetSquare(nSquareNbr);

                oCurrSquare.ClearPiece();
                oCurrSquare.WhiteAttackersCount = 0;
                oCurrSquare.BlackAttackersCount = 0;
            }
        }

        private void ClearSquareAttackers()
        {
            for (int nSquareNbr = 0; nSquareNbr < Nomenclature.BOARD_SIZE * Nomenclature.BOARD_SIZE; nSquareNbr++)
            {
                Square oCurrSquare = GetSquare(nSquareNbr);

                oCurrSquare.WhiteAttackersCount = 0;
                oCurrSquare.BlackAttackersCount = 0;
            }
        }

        private bool HasInterveningPiece(
            int nStartSquareNbr
            , int nEndSquareNbr
            )
        {
            Square oCurrSquare = GetSquare(nStartSquareNbr);
            if (oCurrSquare.Piece is Pieces.WhiteKnight
                || oCurrSquare.Piece is Pieces.BlackKnight
                )
            {
                return false;
            }

            int nStartRank = Base.Business.Utilities.GetSquareRank(nStartSquareNbr);
            int nStartFile = Base.Business.Utilities.GetSquareFile(nStartSquareNbr);
            int nEndRank = Base.Business.Utilities.GetSquareRank(nEndSquareNbr);
            int nEndFile = Base.Business.Utilities.GetSquareFile(nEndSquareNbr);
            int nRankOffset = (nStartRank < nEndRank) ? 1 : ((nStartRank == nEndRank) ? 0 : -1);
            int nFileOffset = (nStartFile < nEndFile) ? 1 : ((nStartFile == nEndFile) ? 0 : -1);

            while (
                nStartRank + nRankOffset != nEndRank
                || nStartFile + nFileOffset != nEndFile
                )
            {
                nStartRank += nRankOffset;
                nStartFile += nFileOffset;

                oCurrSquare = GetSquare(
                    nStartRank
                    , nStartFile
                    );

                if (oCurrSquare.HasPiece())
                {
                    return true;
                }
            }

            return false;
        }

        private void CalculateSquareAttackers()
        {
            ClearSquareAttackers();

            foreach (Pieces.BasePiece item in m_arrPieces)
            {
                int nCurrSquareNbr = item.Square.SquareNbr;
                List<int> arrAttackedSquares = item.GetAttackedSquares();

                foreach (int nAttackedSquareNbr in arrAttackedSquares)
                {
                    if (HasInterveningPiece(nCurrSquareNbr, nAttackedSquareNbr))
                    {
                        continue;
                    }

                    Square oCurrSquare = GetSquare(nAttackedSquareNbr);

                    //if (nCurrSquareNbr != nAttackedSquareNbr
                    //    && oCurrSquare.HasPiece() 
                    //    && oCurrSquare.Piece.PieceColor == item.PieceColor
                    //    )
                    //{
                    //    continue;
                    //}

                    if (item.PieceColor == Nomenclature.PieceColor.White)
                    {
                        oCurrSquare.WhiteAttackersCount++;
                    }
                    else if (item.PieceColor == Nomenclature.PieceColor.Black)
                    {
                        oCurrSquare.BlackAttackersCount++;
                    }
                    else
                    {
                        throw new InvalidEnumArgumentException("[DEV] There is no such Piece Color.");
                    }
                }
            }
        }

        private void MovePiece()
        {
            MovePiece(
                m_nMoveStartSquareNbr.Value
                , m_nMoveEndSquareNbr.Value
                );

            m_nMoveStartSquareNbr = null;
            m_nMoveEndSquareNbr = null;
        }
        private void MovePiece(
            int nStartSquare
            , int nEndSquare
            )
        {
            Square oStartSquare = GetSquare(nStartSquare);
            Square oEndSquare = GetSquare(nEndSquare);
            Pieces.BasePiece oMovedPiece = oStartSquare.Piece;

            if (oEndSquare.HasPiece())
            {
                m_arrPieces.Remove(oEndSquare.Piece);
            }

            oStartSquare.ClearPiece();
            oStartSquare.HideMoveMarker();
            oEndSquare.SetPiece(oMovedPiece);
            oEndSquare.HideMoveMarker();
        }

        private Square GetSquare(string sSquareName)
        {
            return (Square)LogicalTreeHelper.FindLogicalNode(this, sSquareName);
        }
        private Square GetSquare(int nSquareNbr)
        {
            string sSquareName = Base.Business.Utilities.GetSquareName(nSquareNbr);
            return (Square)LogicalTreeHelper.FindLogicalNode(this, sSquareName);
        }
        private Square GetSquare(
            int nRank
            , int nFile
            )
        {
            string sSquareName = Base.Business.Utilities.GetSquareName(
                nRank 
                , nFile
                );
            return (Square)LogicalTreeHelper.FindLogicalNode(this, sSquareName);
        }
    }
}
